#pragma once

#include <vex/core/implementation/handle_from_base.h>
#include <utility>
#include <exception>

namespace vex {

    namespace core {
        using implementation::handle_from_base;

#ifdef VEX_HAS_CPP_11_FEATURE_SET_3 
        /// <summary>
        /// Create instance of a handle enabled type from given argument list 
        /// using default allocator and default ownership policy (variadic version)
        /// </summary>
        template < class TBase, class ...TArgs > auto make_raw_handle(TArgs&& ...p_args)
            -> handle_from_base<TBase>*
        {
            try {
                return new handle_from_base<TBase>(std::forward<TArgs>(p_args)...);
            } catch (std::exception &) {
                return nullptr;
            }
        }

        /// <summary>
        /// Create instance of a handle enabled type from given argument list 
        /// using default allocator and given ownership policy (variadic version)
        /// </summary>
        template < class TBase, class TOwnership, class ...TArgs > auto make_raw_handle_ext(TArgs&& ...p_args)
            -> handle_from_base<TBase, TOwnership>*
        {
            try {
                return new handle_from_base<TBase, TOwnership>(std::forward<TArgs>(p_args)...);
            } catch (std::exception &) {
                return nullptr;
            }
        }
#else
        /// <summary>
        /// Create instance of a handle enabled type from given argument list 
        /// using default allocator and default ownership policy
        /// </summary>
        template < class TBase > handle_from_base<TBase>* make_raw_handle()
        {
            try {
                return new handle_from_base<TBase>;
            } catch (std::exception &) {
                return nullptr;
            }
        }

        template <class TBase, class TArg1>
        handle_from_base<TBase>* make_raw_handle(
            TArg1&& p_arg1)
        {
            try {
                return new handle_from_base<TBase>(
                    std::forward<TArg1>(p_arg1));
            } catch (std::exception &) {
                return nullptr;
            }
        }

        template <class TBase, class TArg1, class TArg2>
        handle_from_base<TBase>* make_raw_handle(
            TArg1 && p_arg1, TArg2 && p_arg2) 
        {
            try {
                return new handle_from_base<TBase>(
                    std::forward<TArg1>(p_arg1), 
                    std::forward<TArg2>(p_arg2));
            } catch (std::exception &) {
                return nullptr;
            }
        }

        template <class TBase, class TArg1, class TArg2, class TArg3>
        handle_from_base<TBase>* make_raw_handle(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3) 
        {
            try {
                return new handle_from_base<TBase>(
                    std::forward<TArg1>(p_arg1), 
                    std::forward<TArg2>(p_arg2), 
                    std::forward<TArg3>(p_arg3)); 
            } catch (std::exception &) {
                return nullptr;
            }
        }

        template <class TBase, class TArg1, class TArg2, class TArg3, class TArg4>
        handle_from_base<TBase>* make_raw_handle(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4) 
        {
            try {
                return new handle_from_base<TBase>(
                    std::forward<TArg1>(p_arg1), 
                    std::forward<TArg2>(p_arg2), 
                    std::forward<TArg3>(p_arg3), 
                    std::forward<TArg4>(p_arg4)); 
            } catch (std::exception &) {
                return nullptr;
            }
        }

        /// <summary>
        /// Create instance of a handle enabled type from given argument list 
        /// using default allocator and given ownership policy
        /// </summary>
        template <class TBase, class TOwnership> handle_from_base<TBase, TOwnership>* make_raw_handle_ext()
        {
            try {
                return new handle_from_base<TBase, TOwnership>;
            } catch (...) {
                return nullptr;
            }
        }

        template <class TBase, class TOwnership, class TArg1> handle_from_base<TBase, TOwnership>* make_raw_handle_ext(
            TArg1&& p_arg1)
        {
            try {
                return new handle_from_base<TBase, TOwnership>(
                    std::forward<TArg1>(p_arg1));
            } catch (...) {
                return nullptr;
            }
        }

        template <class TBase, class TOwnership, class TArg1, class TArg2> handle_from_base<TBase, TOwnership>* make_raw_handle_ext(
            TArg1 && p_arg1, TArg2 && p_arg2)
        {
            try {
                return new handle_from_base<TBase, TOwnership>(
                    std::forward<TArg1>(p_arg1), 
                    std::forward<TArg2>(p_arg2));
            } catch (...) {
                return nullptr;
            }
        }

        template <class TBase, class TOwnership, class TArg1, class TArg2, class TArg3> handle_from_base<TBase, TOwnership>* make_raw_handle_ext(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3) 
        {
            try {
                return new handle_from_base<TBase, TOwnership>(
                    std::forward<TArg1>(p_arg1), 
                    std::forward<TArg2>(p_arg2), 
                    std::forward<TArg3>(p_arg3)); 
            } catch (...) {
                return nullptr;
            }
        }

        template <class TBase, class TOwnership, class TArg1, class TArg2, class TArg3, class TArg4> handle_from_base<TBase, TOwnership>* make_raw_handle_ext(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4) 
        {
            try {
                return new handle_from_base<TBase, TOwnership>(
                    std::forward<TArg1>(p_arg1), 
                    std::forward<TArg2>(p_arg2), 
                    std::forward<TArg3>(p_arg3), 
                    std::forward<TArg4>(p_arg4)); 
            } catch (...) {
                return nullptr;
            }
        }
#endif
    }

    using core::make_raw_handle;
    using core::make_raw_handle_ext;
}